/**
 * Copyright (c) 2012-2014, Yang Yang <cntototo@gmail.com>
 *
 * This software may be distributed under the terms of the New BSD License.
 * See README for more details.
 */

#include "wfs_api.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include <assert.h>

#include "version.h"

#define CLI_TAG "WFS-CLI: "
#define IS_WHITE(c) ((c) == ' ' || (c) == '\t' || (c) == '\n')

extern int log_level;

/***********************************************************
 *                    Local variables
 ***********************************************************
 */
static const char *wfs_cli_version =
"wfs_cli v" VERSION "\n"
"Copyright (c) <2014>, Yang Yang <cntototo@gmail.com>";

static const char *wfs_cli_license =
"This software may be distributed under the terms of the New BSD License.\n"
"See README for more details.";

static const char *wfs_cli_usage =
"usage:\n"
"  wfs_cli -i <ifname> [-hv]";

static const char *wfs_cli_options =
"options:\n"
"  -h = show help information\n"
"  -v = show version";

static const char *wfs_cli_example =
"example:\n"
"  wfs_cli -i wlan0"; /* TODO: example string */

static struct wfs_handle *handle;

/***********************************************************
 *                    Local functions
 ***********************************************************
 */
static void usage(void)
{
	/** Version and license */
	printf("%s\n\n%s\n\n", wfs_cli_version, wfs_cli_license);
	/** Usage */
	printf("%s\n\n", wfs_cli_usage);
	/** Option */
	printf("%s\n\n", wfs_cli_options);
	/** Example */
	printf("%s\n", wfs_cli_example);
}


static void terminate(int sig_num)
{
	wfs_disconnect(handle);
}


static int formalize(char *buf, size_t len)
{
	int ret = 0; /* start idx */

	assert(buf);

	if (len == 0)
		return ret;

	/** Currently only strip prefix spaces */
	while (IS_WHITE(buf[ret]))
		++ret;

	return (ret >= len) ? -1 : ret;
}


static enum wfs_error send_cmd(struct wfs_handle *handle, struct wfs_msg *cmd)
{
	enum wfs_error ret = WFS_ERR_SUCCESS;
	char buf[4096];
	struct wfs_msg resp;

	assert(handle && cmd);

	memset(buf, 0, sizeof(buf));
	resp.len = sizeof(buf);
	resp.body = buf;

	ret = wfs_send(handle, cmd, &resp);

	if (resp.len) {
		printf("%s\n", resp.body);
		fflush(stdout);
	}

	return ret;
}


static void go_interactive(struct wfs_handle_params *params)
{
	fd_set readfds;
	int max_fd = 0;
	int ret = 0;
	char line[512];
	char buf[4096];
	struct wfs_msg cmd;
	struct wfs_msg event;
	int evt_sock = -1;
	int start_idx = 0;

	assert(params);

	/** Connect to WFS daemon */
	handle = wfs_connect(params);
	if (!handle) {
		printf("Failed to connect to WFS daemon. "
			"Is WFS daemon running?\n");
		return;
	} else {
		printf("To exit, press ctrl + C\n\n");
		printf("Please type your command now. "
			"Examples are given in wfs_commands doc\n");
	}

	if (fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK) < 0) {
		perror("fcntl");
		goto out;
	}

	evt_sock = wfs_get_evt_sock(handle);
	if (evt_sock < 0) {
		printf(CLI_TAG "Invalid event socket.\n");
		goto out;
	}

	max_fd = (STDIN_FILENO > evt_sock) ? STDIN_FILENO : evt_sock;

	printf("> ");
	fflush(stdout);

	for (;;) {
		FD_ZERO(&readfds);
		FD_SET(STDIN_FILENO, &readfds);
		FD_SET(evt_sock, &readfds);

		ret = select(max_fd + 1, &readfds, NULL, NULL, NULL);
		if (ret < 0 && errno == EINTR)
			/** Interrupted, such as ctrl-C */
			return;
		if (ret < 0) {
			perror("select");
			goto out;
		}

		/** Check stdin */
		if (FD_ISSET(STDIN_FILENO, &readfds)) {
			/** Receive user input command */
			memset(line, 0, sizeof(line));
			ret = read(STDIN_FILENO, line, sizeof(line) - 1);
			if (ret < 0) {
				perror("read");
				goto out;
			}

			/** Formalize user input */
			start_idx = formalize(line, strlen(line));

			/** Send command to WFS daemon */
			if (start_idx >= 0) {
				cmd.len = strlen(line) + 1 - start_idx;
				cmd.body = line + start_idx;
				if (send_cmd(handle, &cmd) != WFS_ERR_SUCCESS) {
					printf(CLI_TAG "Failed to send "
						"command %s\n", line);
					goto out;
				}
			}

			/** Show prompt */
			printf("> ");
			fflush(stdout);
		}

		/** Check event sock */
		if (FD_ISSET(evt_sock, &readfds)) {
			/** Receive event */
			memset(buf, 0, sizeof(buf));
			event.len = sizeof(buf);
			event.body = buf;
			if (wfs_recv(handle, &event) != WFS_ERR_SUCCESS) {
				printf(CLI_TAG "Failed to receive event.\n");
				continue;
			}
			if (event.len)
				printf("\nRECEIVED EVENT: %s\n", event.body);
		}
	}

out:
	wfs_disconnect(handle);
}


/***********************************************************
 *                    Main entrance
 ***********************************************************
 */

int main(int argc, char *argv[])
{
	int exitcode = 0;
	int opt = 0;
	int has_ifname = 0;
	struct wfs_handle_params params;

	memset(&params, 0, sizeof(params));

	for (;;) {
		opt = getopt(argc, argv, "hi:v");
		if (opt < 0)
			break;

		switch (opt) {
		case 'h':
			usage();
			goto out;
		case 'i':
			has_ifname = 1;
			strncpy(params.ifname, optarg, 16);
			break;
		case 'v':
			printf("%s\n", wfs_cli_version);
			goto out;
		default:
			usage();
			goto out;
		}
	}

	/** Suppress debug print of WFS API */
	log_level = 0;

	if (!has_ifname) {
		usage();
		goto out;
	}

	if (optind >= argc) {
		signal(SIGHUP, terminate);
		signal(SIGINT, terminate);
		signal(SIGTERM, terminate);
		go_interactive(&params);
	} else {
		/** TODO: send cmd one shot */
	}

out:
	return exitcode;
}
